package com.backpackerxl.blog.service.impl;

import com.backpackerxl.blog.dao.BlogRepository;
import com.backpackerxl.blog.dao.ViewsRepository;
import com.backpackerxl.blog.model.Blog;
import com.backpackerxl.blog.model.Type;
import com.backpackerxl.blog.model.Views;
import com.backpackerxl.blog.service.BlogsService;
import com.backpackerxl.blog.util.MarkdownUtils;
import com.backpackerxl.blog.util.MyBeanUtils;
import com.backpackerxl.blog.vomodel.BlogCount;
import com.backpackerxl.blog.vomodel.BlogQuery;
import com.backpackerxl.blog.web.NotFoundException;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.Join;
import javax.persistence.criteria.Predicate;
import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * Author by：Backpackersxl
 * Date: 2021/5/7/007
 * Time: 17:59
 **/
@Service
public class BlogsServiceImp implements BlogsService {
    private final BlogRepository blogRepository;
    private final ViewsRepository viewsRepository;

    public BlogsServiceImp(BlogRepository blogRepository, ViewsRepository viewsRepository) {
        this.blogRepository = blogRepository;
        this.viewsRepository = viewsRepository;
    }

    @Override
    public Blog getBlog(Long id) {
        return blogRepository.findById(id).orElse(null);
    }

    @Override
    public Page<Blog> listBlog(Pageable pageable, BlogQuery blog) {

        return blogRepository.findAll((Specification<Blog>) (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (!"".equals(blog.getTitle()) && blog.getTitle() != null) {
                predicates.add(criteriaBuilder.like(root.get("title"), "%" + blog.getTitle() + "%"));
            }
            if (blog.getTypeId() != null) {
                predicates.add(criteriaBuilder.equal(root.<Type>get("type").get("id"), blog.getTypeId()));
            }
            if (blog.isRecommend()) {
                predicates.add(criteriaBuilder.equal(root.<Boolean>get("recommend"), blog.isRecommend()));
            }
            query.where(predicates.toArray(new Predicate[0]));
            return null;
        }, pageable);
    }

    @Transactional
    @Override
    public Blog saveBlog(Blog blog) {
        if (blog.getId() == null) {
            blog.setId(0L);
            blog.setCreateTime(new Date());
            blog.setUpdateTime(new Date());
            blog.setViews(0);
        } else {
            blog.setUpdateTime(new Date());
        }

        return blogRepository.save(blog);
    }

    @SneakyThrows
    @Transactional
    @Override
    public Blog updateBlog(Long id, Blog blog) {
        Blog b = blogRepository.findById(id).orElse(null);
        if (b == null) {
            throw new NotFoundException("该博客不存在");
        }
        BeanUtils.copyProperties(blog, b, MyBeanUtils.getNullPropertyNames(blog));
        b.setUpdateTime(new Date());
        return blogRepository.save(b);
    }

    @Transactional
    @Override
    public void deleteBlog(Long id) {
        blogRepository.deleteById(id);
    }

    @Override
    public Page<Blog> listBlog(Pageable pageable) {
        return blogRepository.findAllByPublished(pageable);
    }

    @Override
    public Page<Blog> listBlog(Long tagId, Pageable pageable) {
        return blogRepository.findAll((Specification<Blog>) (root, query, criteriaBuilder) -> {
            Join<Object, Object> join = root.join("tags");
            return criteriaBuilder.equal(join.get("id"), tagId);
        }, pageable);
    }

    @Override
    public List<Blog> listBlognew(Integer num) {
        Sort sort = Sort.by(Sort.Direction.DESC, "updateTime");
        Pageable pageable = PageRequest.of(0, num, sort);
        return blogRepository.findTop(pageable);
    }

    @Override
    public Map<String, List<Blog>> archivesBlog() {
        List<String> years = blogRepository.findGroupYear();
        Map<String, List<Blog>> map = new LinkedHashMap<>();
        for (String year : years) {
            map.put(year, blogRepository.findByYear(year));
        }
        return map;
    }

    @Override
    public Long countBlog() {
        return blogRepository.countByPublished();
    }

    @Override
    public Page<Blog> listBlogSearch(String query, Pageable pageable) {
        return blogRepository.findByQuery(query, pageable);
    }

    @SneakyThrows
    @Override
    public Blog getAndConvert(Long id, HttpServletRequest request) {
        Blog blog = blogRepository.findById(id).orElse(null);
        if (blog == null || !blog.isPublished()) {
            throw new NotFoundException("博客不存在");
        }
        Blog b = new Blog();
        BeanUtils.copyProperties(blog, b);
        String content = b.getContent();
        b.setContent(MarkdownUtils.markdownToHtmlExtensions(content));
        setViews(id, request);
        return b;
    }

    @Async
    public void setViews(Long id, HttpServletRequest request) {
        String ip = getIp(request);
        Views see = viewsRepository.findByIpAndBlogId(ip, id);
        if (see != null) {
            Calendar ins = Calendar.getInstance();
            ins.setTime(see.getRecordTime());
            ins.add(Calendar.DAY_OF_MONTH, 1);
            // 超过7天，重新记录
            if (ins.getTime().getTime() <= new Date().getTime()) {
                see.setRecordTime(new Date());
                viewsRepository.save(see);
                // 更新博客的浏览次数
                blogRepository.updateViews(id);
            }
            return;
        }
        blogRepository.updateViews(id);
        Views views = new Views();
        views.setIp(ip);
        views.setBlogId(id);
        views.setRecordTime(new Date());
        viewsRepository.save(views);
    }

    /**
     * 获取请求ip
     *
     * @param request 请求
     * @return ip
     */
    private String getIp(HttpServletRequest request) {
        String ipAddress = request.getHeader("X-Forwarded-For");
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
        }
        return ipAddress.split(",")[0];
    }

    @Override
    public Long countBlogs() {
        return blogRepository.count();
    }

    @Override
    public List<BlogCount> listBlogViewCount() {
        return blogRepository.findBlogViewCount();
    }

}
